home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Disc to the Future 2
/
Disc to the Future Part II Programmer's Reference (Wayzata Technology)(6013)(1992).bin
/
MAC
/
THINKC
/
3_0
/
OIC_1
/
EXPLORER.S_K
< prev
next >
Wrap
Text File
|
1989-03-07
|
42KB
|
991 lines
O.I.C Objects-in-C (V1.02)
===================
Explorer's Kit Documentation
Copyright ⌐ John Wainwright, 1988, 1989
454 West 20th Street, #2
New York, NY 10011
Compuserve : 72657,2534
All rights reserved
Shareware fee : $20
This is the shareware, explorer's release of a portable
object-oriented programming environment for the C pro-
gramming language. The kit contains the complete objects
environment, a bunch of simple, but useful demo classes
and this, EXPERTS ONLY, documentation - enough for ex-
perienced C programmers that know something about object-
oriented techniques to take advantage of them in the
excellent C environments now on the Mac.
It is supplied as a verion 3, LightSpeed C project but
the source files are simple text files that should be
readable by any of the Macintosh C compilers.
The full release will come with complete documentation
and an EXTENSIVE set of classes for developing Mac
applications - a persistent object store, a complete
Model-View-Controller system for accessing the Mac
toolbox, 2D & 3D PHIGGS-like imaging spaces integrated
with the MVC system, a polymorhpic, object-oriented
spreadsheet system, and an embeddable Common Lisp
subset interpreter with OIC interface.
Acknowledgements: Tim Long and Andrew Nicholson
both contributed materially to
the ideas in this system.
** corresponds to version V1.02 released on Mar 5th, 1989 **
** the V1.01 changes are marked with change bars **
Introduction
OIC is a state-of-the-art object-oriented programming environment for
the C programming language implemented as a portable library of
management routines and a set of programming conventions. It
features the following :
Ñ A fully dynamic class structure
Ñ Multiple inheritence
Ñ Class variables & class methods
Ñ Generic function method dispatch with method caching
Ñ True run-time polymorphism
Ñ Support for multi-methods
Ñ No need for a special pre-processor - will work with ANY C
Ñ High speed access to instance variables via C structures
Ñ Method dispatch tracing system
Ñ Smooth & arbitrary transition between C & OOP code
OIC has more of the flavor of the dynamic, Lisp-based OOPS &
SmallTalk than of the static languages like C++ and Object Pascal.
Some of the features, like generic functions, are reasonably
avant-garde, and are described in the following sections.
There is one major departure from the semantics of these OOP systems
as a consequence of using no pre-processor & deciding to access
instance variables via C structures: instance variables effectively
have a SINGLE level of scoping. See the section on Limitations, below
for a full discussion.
This document assumes a detailed familiarity with C and a reasonable
understanding of object-oriented programming techniques. For those
without the latter, try "Object-Oriented Programming for the
Macintosh" by Kurt Schmucker or "Object-Oriented Programming in
Common Lisp" by Sonya Keene. The following sections describe the
implementation approach & memory structures, OIC limitations and
the example file set. At the end is a reference list of library
functions & macros, some warnings about portability limitations,
and details of the shareware licence.
------------------
Implementation
This section describes the 3 prime elements of the OIC
system :
Ñ object layout, management and access
Ñ class layout, management and access
Ñ generic function & method creation and dispatch
All 3 involve structures created & managed at run-time by the OIC
library. In this version, ALL the structures are allocated as
handles, locked & then dereferenced. This is NOT good form, but
seems to be the quickest memory management scheme on the Mac. If
it offends you, the storage allocation routines in "memory.c" can
be changed to suit. The decision to use pointers & not handles
was to promote portability to C environments other than on the
Macintosh; a future version may use handles.
Objects
Objects are layed-out in memory as a class tag followed by
the concatenation of all the instance variables (IVs) defined
in the object's component classes - i.e., its class and all
its superclasses, and all their super classes, etc. Each
object is allocated as a single block of memory and the IVs
are zero-filled. The class tag defines the object's class
and is actually a pointer to the class's defining structure
(described below). The IV's defined by each component class
are mapped & accessed by ordinary C structures - when a method
is invoked, a pointer is supplied that points to the local
IV structure, i.e., the IVs defined in the class in which the
invoked method was found.
Objects are always referred to by pointers. The provided
"object" typedef declares a pointer storage type that will
hold a pointer to ANY class of object - the system is
dynamically polymorhpic. IVs can be any C type including,
of course, object.
Objects are created with the "New" function. It takes a
class & any number of following arguments. When created,
the object has its "new" generic function invoked which
is passed the other, presumably initialization, arguments.
It is the programmers responsibilty to explicitly free
memory occupied by unused objects. The generic function
"dispose", a method for which is provided in default by
the root class "Object", will free the originally
allocated object. This should be specialised
if any other housekeeping is needed. By convention,
the generic "deepDispose" will free the object and,
recursively, any contained objects. This, for example,
is how the supplied List class lists are freed in one go.
------------------
Classes
OIC provides full multiple inheritence of methods. This is
supported by a run-time collection of "Class" objects (yes,
classes are proper objects) arranged in directed, acyclic
graph. This D.A.G. maps the inheritence relationships
among the classes. All classes inherit eventually from
the root class "Object" which provides a bunch of useful
default methods. (see "object.c" for its definition).
Each object is tagged by a pointer to its class's object.
This is how OIC determines an object's class and contrives
to dispatch the correct method for a given generic function
invokation. Each class object contains instance allocation
and IV mapping information, an immediate superclass list,
the class variables and method invokation information.
Clearly, a class must exist before any instances of it
can be created.
The general intention is that each class be defined in a
separate source file (see examples) to promote information
hiding, functional independance & all those good things.
Each file contains the IV & CV structure definitions, a global
to hold the class object, the methods which are written as
static functions, and an initializing function that creates
the class and associates its methods with their generic
functions (see below).
A class is created by the "NewClass" function. It takes as
arguments, the size of the IV & CV structures, a class name
(as a C string) and a null-terminated list of superclasses.
The order of this list determines the inheritence priority.
The supplied method finder does a breadth-first search up
the superclass tree, scanning across the superclass lists
at each level in the order in which they were specified to
the NewClass function. Clearly also, "super"classes
must exist before they can be specialized or mixed-in.
OIC supports class variables and class methods - see the
"IndexMixin" class for an example of how they work.
------------------
Generic Functions and Methods
OIC uses the Generic Function (GF) technique of method dispatch,
prefered in the current state-of-the-art LISP object-oriented
systems like CLOS, New Flavors, Common Loops, and Object LISP.
It has several semantic & performance advantages over the
message passing schemes and embeds exceptionally cleanly in
functional languages (like C).
Instead of sending a message identifier to an object as in
SmallTalk, or qualifying a function name by a class name as in
Object Pascal, the generic function is a single function
defined over a number of argument classes. It acts like
a dispatcher which invokes the appropriate method (or methods)
depending on the class of one (or more) of its arguments and
hence is "generic". In the terminology a "generic function"
embodies many operations depending on the class of its
arguments. The different operations are defined by "method
functions", one in each of the classes that will directly
handle that generic.
For example, in a messaging-passing system we might have :
send("append", list, item);
In a GF system this becomes :
append(list, item);
"append" being a function which dispatches to one of the
various appending methods depending on the class of its first
argument. This is clearly the preferred form for embedding
in C. Apart from looking good, a number of neat things follow
from this technique.
Firstly, it is very simple to write high-speed, caching
method dispatchers, usually requiring only a single indexing
operation to get the method required.
Secondly, individual generic functions can be made to have
their own dispatching semantics by allowing programmers to
straight-forwardly define their own dispatching functions.
For example, generic functions can be cleanly made to be driven
by the classes of several arguments, not just one, allowing
the implementation of so-called multi-methods. In a
mixed-mode arithmetic system, we might have something like :
add(a, b);
where a & b have different classes & hence some coercion is
required. A dispatching function for a set of math generic
functions might look at the classes of the arguments, decide
on the mutually widest class, invoke coercion generics on
a & b to that wider class & invoke the add generic on the
coercions :
add(coerce(a, wide), coerce(b, wide));
Another example might be a dispatching function that will handle
before, after & around daemon methods in the way of ZetaLisp's Flavors.
Defining Generic Functions
OIC comes supplied with only a single kind of generic function;
one that dispatches on the class of the first argument -
equivalent to simple message-passing. If inheritence is
needed it follows a BREADTH-FIRST search up the superclass tree
caching any found methods for subsequent single-index dispatch.
Generic functions of this simple type are defined using the
"defGeneric" macro (defined in "object.h"). This macro is
unfortunately very unwieldly - it requires 3 arguments which
are just variants of the generic's name (if ONLY one could
construct symbols & strings in the standard C preprocessor!!!)
It declares a function with the generic's name having the
above-mentioned dispatching semantics and declares some tables
that hold pointers to the method functions. This method
table is used by the dispatching code and is in two parts,
one for normal methods & the other for class methods; they
are both indexed by a unique "class index" which is allocated
when the class is created.
Other kinds of dispatching semantics would be implemented by
defining and using variants of the defGeneric macro. The
technique allows multiple concurrent defGeneric variants.
Defining & Invoking Method Functions
As mentioned in the Class section above, the various methods
for a class are usually defined as static functions in that
class's source file. Method functions are passed 3
arguments on invokation: the dispatching object, a pointer
to the local IV structure in that object, and a pointer
to the rest of the arguments given to the generic. For
example, the generic invokation :
set(window1, "Untitled", &rect1, VISIBLE);
would result in calling a method function declared :
static object
set_method(self, ivs, args)
object self;
window_iv *ivs;
struct
{
char *title;
Rect *frame;
int visFlag;
} *args;
{
.....
ivs->title = args->title;
.....
set_frame(self, args->frame);
.....
return self;
}
such that "self" points to the key object "window1",
"ivs" points to the "window_iv" structure within that
object (remember all the IV structures of the component
classes are concatenated to form an object), and "args"
points to the string, rectangle & visibility arguments.
The "args" argument actually points into the call frame
of the generic function "set" at its secondary arguments
on the stack. This is done for speed & so the dispatcher
doesn't have to know how many arguments there are. This is
guaranteed to work only in C implementations that use
the stack for passing arguments (all the C's on the Mac).
Some C's on large machines with millions of registers use
them instead of the stack for argument passing and OIC's
technique wont work (see portability limitations, below).
Also, on those C's that use the stack, structures are
layed-out in the same way as arguments on the stack,
so the in-line argument structure declaration is a neat
way of getting at these "passed-on" arguments. Remember,
though, that chars are widened to ints and floats to doubles
when being placed on the argument stack - they must be
declared as this widened form in the argument mapping
struct.
Note in the example, above, how the IVs & arguments are
accessed in clean, high-speed ways.
Associating Methods with Generic Functions
A generic function is implemented by a bunch of method
functions, one from each of the classes that is so
inclined. The association of a method with its generic
is usually done in the class source file, in the initializing
function that creates the class (see the example classes).
The function "AddMethod" performs this task and is tuned
to take a bunch of methods defined in one class & associate them
one-by-one with each's particular generic. It takes a class object
as its first argument followed by a null terminated list of
pairs: the generic's method table (declared by "defGeneric") and
the method function, e.g.
AddMethods(List, appendGeneric, appendMethod,
headGeneric, headMethod,
tailGeneric, tailMethod,
....,
NULL);
AddMethods can be called any time and any number of times after
a class has been created to incrementally add its methods. An
equivalently argumented function called "AddClassMethods" will
add class methods to the generics specified. (see IndexMixin.c
for an example).
------------------
Limitations
Embedding such a system in standard C necessarily involes trade-
offs. Some of these have resulted in limitations and others
in the need for rather cryptic code.
Scope and Inheritence of Instance Variables
The decision to map object instance variables by C
structures results in the only major departure from
conventional OOPS semantics - the SCOPE of instance
variables is effectively limited to the level in the class
hierarchy that defines them. This single level of
scoping immediately implies that ALL instance & class
variables are inherited; there is no "hiding" of similarly-
named instance variables up the inheritance chain
as occurs in some other OOP systems.
Some would contend that this is due & proper adherence
to the programming principles of encapsulation and,
fortunately, it rarely causes problems - one tends to be
interested in the goings-on of the IVs in the
methods of the class that defined the IVs. However,
on some occasions, especially when slightly specialising
an existing class, it is handy to be able to get at the
IVs for that class, and there are 2 ways to achieve this.
The first is to use one of the macros "localIVs" or "myIVs".
The first allows you to reach into any object of the same
class as "self" and get at the IV structure for the current
method's class. "myIVs" reaches into "self" and gets the
IVs for any one of its superclasses. See Macros, below for
further details.
The second way is to provide methods to access IVs. Most
of the Lisp OOPS do it this way always, the OOPS system
automatically generating the accessing methods - very easy
to do in Lisp; not so in C. This technique can, of course,
be used on an IV-by-IV basis: provide access methods for
only those that will need to be accessed by specializing
classes. Because inherited methods have a nested scoping -
closer ones hide ones farther up the inheritence chain - this
technique also provides a way of implementing nested
inheritence of selected IVs.
------------------
Source File Structure & Coding Conventions
In the example classes and according to the recommended way of
using OIC there is one source file for each class. This
yields a convenient modularity and allows the desired
kinds of information hiding to be set up. Each class
source file usually contains :
1. The IV & (possibly) CV structure definitions.
They will need to be moved out into an include
file if several classes (which should always
be inheriting classes) are to get at them.
2. A class global to hold the class object.
3. The methods defined in this class, usually
as static functions. Note in the example
classes that these are named with the
generic name prefixed by an underscore; you
might be happier with the generic name post-
fixed with "Method" or whatever; in any event,
they must be different from the actual generic
function's name.
4. Any local utility functions that the above might use.
5. An initializing function that creates the class
and associates its methods with their corresponding
generics. This function needs to be called during
initialization in the main code.
The declaring of generic functions requires some care. They
are intended to be program-wide globals and are probably best
all declared in one spot because almost any class might
wind up defining a method for one. In the example file set
they are all declared in "generics.c". They also need to be
declared external in all the source files that might use
them; in the example kit this is done in the include file
"generics.h".
Any file that uses OIC should include "oic.h" - it defines
all the OIC typedefs, macros & externals.
------------------
Example File Set
This explorer's kit is intended primarily to place a working
object-oriented C programming mechanism into the hands of
expert C programmers for their edification, enjoyment and,
hopefully, serious use. The demo classes and small test
main program are not intended to be a tutorial on OOPS
programming, nor to necessarily form the basis for a serious
suite of classes. They are simply the test classes I built
as OIC came together, suffer somewhat from its growing
pains, and are provided to help illuminate the basic
object manager.
The base object manager is represented in the files :
oic.c - the basic management routines
memory.c - customizable object allocation routines
oic.h - the include file for anybody using oic
Object.c - the root class
Class.c - the class class
Certain of the class files go to make up the so-called
system classes :
List.c - a general purpose list class
String.c - a class to hold C strings
Replist.c - a representation class for nested objects
Collect.c - a generic superclass that adds lots of
neat collection methods to any class that
provides head, tail, push & eq
List2.c - a version of the list class using Collect
IndexMixin.c - a mixin (a modifying superclass) that will
keep track of all the instances of a class
that uses it as a superclass
Linkseq.c - a general purpose sequencing class for
classes that understand head, tail & isEmpty]
| DependentsMixin.c - a mixin that keeps a dependents list for
| its inheritors.
| StdioStream.c - provides text I/O
| Name.c - a specialization of string for storing names
| Binding.c - binds a key object to a value object.
| HashTable.c - maintains a hashed table of bindings - a
| lookup table.
| NameSpace.c - a specialization of HashTable that provides
| a nested scoping. It is intended that Names be
| used as keys.
| NameMixin.c - makes instances of inheriting classes nameable
| in a global object name space.
| NameTable.c - a degenerate form of lookup table that uses
| == comparisons (i.e. good for Names) no hashes.
| For fast, small lookup tables.
generics.c - defGenerics for all the above classes
generics.h - external definitions for the above classes
| names.h - external definitions for the NameSpace classes
Notes:
1. The List class does NOT implement standard LISP lists, the
operations all destructively modify the subject list. It
will only hold OIC objects as elements.
2. The IndexMixin class provides an example of how to use
class variables & class methods. It specializes the "new"
method & maintains a list (using the List class) of each
instance of a class inheriting from it in a class variable.
They can be got by invoking "allInstances" on the class you
are keeping track of. It also adds a "sequence" class method
so you can simply sequence over the class. It is an
example of a so-called "mixin" class, not intended to be
used on its own but "mixed-in" with some base class usually
providing some extra behaviour. This kind of class is
only possible in a multiple inheritance system.
3. The Replist class is a specialization of the List class
intended to help formatting & printing nested objects
like Lists. It gets used by the repList methods in various
classes to create a structure isomorphic to the object
being printed but with String representations of the
object's leaf values instead of the values themselves.
This can then be neatly parenthesised & indented, etc. See
how it gets used by looking at the print method in the
root Object class (object.c). This print method is
inherited by EVERY class and so provides a simple
object printer for any class that can generate a Replist
of itself (see List).
4. the Linkseq class is an example of what could be
a suite of sequencing utility classes. Its purpose is
to wrap up an object that is being sequenced over to
provide the sequencing context. Note how it gets used
in the test program, main.c. There are several
"for" statements that invoke the "sequence" generic on
an object. Any object that is sequencable should
define a sequence method that will wrap itself up
in, and return, one of these utility sequence classes.
This class should, in turn, respond to the generics
"start", "next", "moreInSeq", & "restart". Other
kinds of sequence "wrapper" classes might work for
scalars by generating consecutive values, strings by
sequencing through the characters, or arrays by indexing
through their elements. See how neatly (in main.c) we get
general purpose polymorphic sequencing in an ordinary
old C "for" statement.
| 5. The Name class brings a measure of efficiency to the use
| of Strings as names (in symbol tables, etc). Instances
| made with the class method "declare" form a proper set,
| i.e. the string is stored only once and the same string
| yields identically the same object. This allows ==
| comparisons on object pointers for name equality and
| saves space at the same time! In other words, dont use
| the "New" primitive to create instances, use the class
| method "declare".
|
| The HashTable, Binding & NameSpace classes cooperate
| to provide a nestable, hashed run-time symbol table
| capability. They will be used by the Lisp class kit.
A small test program with some other little classes is
contained in the files :
main.c - the main test procedure
Coord.c - a kind of point class
Box.c - a kind of rectangle class
TestWindow.c - a trivial window class that experiments
with keyword arguments!
------------------
OIC System Typedefs
typedef struct class *class - declares a holder for a class object
typedef struct class **object - declares a holder for an object
| typedef GenericTable *generic - declares a generic definition table
| pointer
OIC System Functions
class NewClass(iv_size, cv_size, name, super1, super2, ... NULL)
int iv_size;
int cv_size;
char *name;
class super1, super2, ...;
creates a new class with local IV & CV structure sizes as given
and that inherits from the null-terminated list of superclasses.
The order of this list determines the inheritence priority.
The supplied method finder does a breadth-first search up
the superclass tree, scanning across the superclass lists
at each level in the order in which they were specified to
the NewClass function.
The class name is used for debugging & tracing output.
object New(class, initializing arguments ...)
class class
<any> initializing arguments ...;
creates a new object of the given class. Invokes the "new"
generic on the fresh object, passing it the initialization
arguments.
void AddMethods(class, generic1, method1, generic2, method2, ..., NULL)
class class;
MethodTable generic1, generic2, ...;
<any> (*method1)(), (*method2)() ...;
associates methods for the given class with their generic
functions. The generics are specified by passing the method
table that is created by the "defGeneric" macro (see below).
void AddClassMethods(class, generic1, method1, generic2, method2, ..., NULL)
class class;
MethodTable *generic1, *generic2, ...;
<any> (*method1)(), (*method2)() ...;
associates class methods for the given class with their generic
functions. The generics are specified by passing the method
table that is created by the "defGeneric" macro (see below).
object Super(object, arguments...)
object object;
<any> arguments;
invokes the method for the currently executing generic that is
next higher up the inheritance path than the current method. This
is very useful when one wishes merely to augment the operation
of an inherited method, rather than completely replace it.
| object SuperFrom(class, object, arguments...)
| class class;
| object object;
| <any> arguments;
|
| same as "Super" but the 'class' argument specifies exactly from
| which ancestor class to inherit the method. Very useful when
| using mixin classes to make sure they get a look at "new"s and
| "dispose"s, for example.
|
| All inheritors of DependentsMixin, for example, must use :
|
| SuperFrom(DependentsMixin, self);
|
| in there "dispose" methods to make sure it disposes the dependents
| list.
object SuperPassArgs(object, argumentListPtr)
object object;
<any> *argumentListPtr;
same as "Super" but a pointer to the argument list is given
rather than specifying them in place. This is useful for passing
the argument list pointer that a method normally gets straight
through to the super method.
| object ApplyGeneric(gen, obj, args)
| generic gen;
| object obj;
| <any> *args;
|
| given a generic table reference, dispatches the appropriate
| method on the given object. Takes a pointer to an argument
| list. (not the args, themselves). Often used to 'pass on'
| arguments to another generic:
|
| ApplyGeneric(drawGeneric, self, ap);
int SizeOf(object)
object object;
returns the total size of the object in bytes.
char *ClassNameOf(object)
object object;
returns the class name of the object as a C string.
char *MethodName(generic)
MethodTable *generic;
returns the name of a generic as a C string given the generics
method table (as defined by "defGeneric", see below).
int IsA(object, class)
object object;
class class;
returns true if the object is of the given class.
int IsAKindOf(object, class)
object object;
class class;
returns true if the object is of the given class or inherits from
that class.
int IsObj(object)
object object;
attempts to check whether object is a valid object. It does
this by ensuring that "object" is a valid pointer, points into
the application heap, points at a tag that points at a class
which itself has a tag which must point to the "Class" object.
It can be fooled, so beware.
void InitOIC()
creates & initializes the OIC system. Should be called
EXACTLY once before any of the OIC functionality is used.
void InitSysClasses()
creates & initializes the system classes.
void TraceOn() - turns on generic call tracing
void TraceOff() - turns it off
void TraceObj(object) - traces any generics called on object
void TraceClass(class) - traces any generics ony any "class" object
void dumpClass(class) - debugging class dump
void dumpMethodTable(mth) - debugging method table dump
OIC System Globals
class currentClass; - the class in which the currently executing
method was found.
class classes; - a linked list of all classes created. You can
get a List of the classes by invoking subs(Object);
class Class; - the class class.
class Object; - the root object class. ALL classes inherit
eventually from Object
| generic generics; - linked list of generic definition tables
| object namedObjects; - the NameSpace of named objects (kept by NameMixin)
Object methods
The following methods are provided as defaults by the root class Object.
ALL classes, therefore, support these methods by default inheritence.
| equal(obj1, obj2) - a byte wise comparison of the contents of each
new(obj, args...) - dummy "new" method
className(obj) - class name as a String object
print(obj) - invokes print(repList(obj))
repList(obj) - default object representation. creates a
replist by stepping through obj
sizeof(object) (4 bytes) at a time. If
the current 4 bytes is a valid object
reference recursively calls repList,
otherwise adds a hex representation to
the repList
| map(obj, f, args....) - maps the function "f" over each item in "obj"
| by using "sequence" on "obj".
| forAll(obj, f, args...) - identical to "map".
| forAllGen(obj, gen, argp) - takes a generic table reference & applies
| its generic over the "sequence" of "obj".
| cantDo(obj, gen, argp) - invoked if "obj" can't do generic "gen". Prints
| an error message. This should be specialized by
| classes that want to handle this their own way.
| dispose(obj) - frees the memory occupied by "obj". Invalidates
| the class tag to make it fail any future uses.
Class methods
supers(class) - returns a (List) list of all the classes ancestors
subs(class) - returns a (List) list of all the classes that
inherit from "class"
print(class) - prints the class name
repList(class) - returns the class name as replist
OIC System Macros & Defines
defGeneric(generic, methodTable, name)
declares a generic function and its attendant method table.
This macro must be called once for each unique generic
function in the system - generics are global functions.
The macro is given the generic's name, the name to be used
for the generic's method table, and the generic's name as
a C string constant.
If C's preprocessor were even the tiniest bit smart it would
allow one to create symbols & strings & you have only had to
give defGeneric one argument. Ah, well.
For example :
defGeneric(print, printGeneric, "print");
externGeneric(generic, methodTable)
declares an external reference to a generic function.
Usually both the generic function itself and its
method table need to be accessible, and this macro
eases their declaration. (see generics.h)
For example :
externGeneric(print, printGeneric);
localIVs(obj, structure)
computes a pointer to the IV structure in "obj" for
to the current class. This is useful if I'm working
with two objects of the same class (say, Lists that
I'm concatenating) and I want to reach into the
IV's of both of them (see _join in list.c). The
"structure" argument is just used to cast the
resulting pointer to that which you want (the
structure typedef for the current class's IVs).
myIVs(class, structure)
computes a pointer to the IVs of "self" for the
given "class". This is the macro that gets around
the one-level scoping problem - I can reach back
and get at the IVs of any superclass I want (providing
the structure typedefs are available - you may
want to enforce the hiding by keeping these typedefs
private).
localCVs(class, structure)
as for localIVs but point at the class variables instead.
myCVs(class, structure)
as for myIVs but point at the class variables instead.
| IVs(obj, class, structure)
|
| computes a pointer to the IV structure in "obj" from
| "class". The "structure" argument is just used to
| cast the resulting pointer to that which you want (the
| structure typedef for the current class's IVs).
|
| CVs(obj, class, structure)
|
| same as IVs but returns a pointer to the "class"es
| CV structure.
ClassOf(object)
computes the class of the given object
| method
|
| just a synonym for "static" to make method function
| declarations look more like methods.
CHECK_OBJS
if defined in "oic.c" will cause all oic functions to
check for valid class & object arguments (by using IsObj()).
This is a conditional assembly flag and is set on in the
distributed kit.
| IGNORE_NULL_OBJS
|
| if defined in "oic.c" causes method dispatch to detect
| a NULL pointer as the dispatching object and simply
| return NULL without complaining. This is useful if you
| frequently checking for NULL objects (often in IVs as
| they are initialized NULL) before invoking generics on them.
| But, beware, this prevents CHECK_OBJS from detecting
| NULL objects which is often a frequent bug in early
| development. You will have to choose whether you want the
| convenience of not having to detetct NULL objects or
| the safety of error checking.
END
equivalent to NULL. Used sometimes to mark the end
of argument lists. Another gripe: why didnt they
design C so you could find out how many arguments you
were given - after all, the function call form is
completely polymorphic - I can understand the lack in
a language with compile-time isomorphism checks like
Pascal.
------------------
Portability Considerations
There is really only one major portability consideration and that
involves OIC's expectations about the use of the stack for passing
arguments. It expects all arguments to be placed on the stack
with earlier arguments at lower addresses.
On 680x0s and 80x86s there is no other sensible option and so
OIC should port without trouble on C compilers for those machines
(the techniques have been tried on 68010 Unix 4.2BSD and 80286
Xenix & MS-DOS boxes succesfully).
OIC was developed in THINK's Lightspeed C, an extremely good C
development environment for the Macintosh. It has also run without
change using Aztec C.
The current version does make use of UNIX-like standard I/O calls
for outputing errors and trace messages. Both LSC and Aztec provide
a Unix emulation library for this. You may have trouble if your C
doesn't, in which case you should recode all the printf's &
fprintf's. Indeed, in the full OIC, this use of Unix emulation
will go and be replaced by the use of generic, stream-based text
window classes.
------------------
| Tips on using OIC with the THINK LSC Debugger on the Macintosh
|
| There is one problem and a couple of tricks associated with using the
| THINK C Debugger. The debugger wont correctly "step" into a generic
| function invokation. This is because the source for the generic
| dispatching code is hidden in a macro expansion and the debugger likes
| to have actual source to show as it is stepping. THINK is looking
| at this problem but apparently it is hard to fix. The only thing
| to do at the moment is to manually place a break-point in the method
| you expect the generic to invoke and "go" to it.
|
| Inspecting objects in the data window requires some tricks. If you just
| keep derefering an object pointer you will wind up examining it's
| class structure (which, of course, you may want to do). If however,
| you want to see the IV structure then you should use the IV accessing
| macros; fortunately the data window accepts macros. So, to see the
| local IVs of self, use
|
| localIVs(self, xxxx_i) /* xxxx is the IV structure typedef */
|
| If you want to look inside an arbitrary object, use
|
| IVs(obj, class, xxxx_i)
|
| This will display the IV's of "obj" for the given "class". It will be
| necessary in this case to have the IV structure declaration available.
| It is useful, during debugging, to have one header file containing all
| the IV structure declarations temporarily included in each file for this
| purpose.
------------------
Licensing Terms
This software is distributed as shareware; if you wind up using it
for developing any software, particularly commercial software, please
send the $20 fee to the address below. You will become a registered
user and have access to upgrades & class kits as they become
available.
The software is also distributed AS IS, with no warranty, expressed or
implied, of the correctness or suitability of this software for any purpose.
No responsibility is assumed for damages arising from the use of this
software.
OIC is protected by copyright and all commercial rights are reserved.
Under NO circumstances may this software be distributed in any decodable
source form, or any form that might be usable as a programming tool
for commercial advantage. Reasonable duplication costs can be charged.
Under the shareware ethic, however, you are free to copy & distribute
the OIC software UNCHANGED, providing this document & licensing terms
are also copied & distributed with the OIC software.
Feedback & queries are welcome. I am interested in any classes that
you may develop - indeed, it seems to me that the success
of a system like this will primarily be a function of the quantity and
quality of classes available.
Have fun!
John Wainwright
454 West 20th Street, #2
New York, NY 10011
Compuserve : 72657,2534
All OIC files & documentation are Copyright ⌐ John Wainwright, 1988, 1989